/*
版权@hehung所有，转载请申明
email：1398660197@qq.com
*/
#include "app_rtc.h"
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <board.h>
#include <aos/kernel.h>
#include "app_main.h"
#include <ntp.h>
#include "app_kv.h"
#include "app_weather.h"


#define RTC_NTP_DEBUG                  (STD_OFF)
#define RTC_DEBUG                      (STD_OFF)

#define ONE_DAY_TIMESTAMP              ((time_t)86400U)


static csi_rtc_t rtc;   
#if (RTC_DEBUG == STD_ON)
static aos_task_t rtc_task_h;    /* RTC task handle */
#endif
static aos_task_t ntp_task_h;    /* NTP task handle */
static uint8_t ntp_udt = 0;      /* update NTP time flag */
static uint16_t ntp_task_cycle_ms = 100;    /* 任务切换周期 */

static time_t mon_yday[2][12] =
{
    {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334},
    {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335},
};
const char Days[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
static csi_rtc_time_t current_time;    /* 最新时间 */
static time_t Last_ntp_ts = 0;    /* 记录上一次ntp获取的时间戳 */
#if 0
static time_t cur_ts = 0;         /* 当前时间戳 */
#endif
static csi_rtc_time_t setting_time;    /* 用于设置的时间 */
static uint8_t ntp_enable_flg = STD_ON;    /* NTP使能标志 */
static uint8_t ntp_init_flg = 0;    /* 初始化标志 */

static void Rtc_TaskEntry(void *arg);
static void Rtc_NtpUpdate(void);
static void Rtc_NtpTaskEntry(void *arg);
static int isleap(int year);
static uint8_t Rtc_NtpTimestampJudge(time_t para_ts);
static uint8_t Rtc_UpdateFromNtp(const struct my_tm ntp_t);
static time_t Rtc_GetCurTimeStamp(void);
static void Rtc_SettingLgc(uint16_t *dt, uint8_t setting_dir, uint16_t limit_up, uint16_t limit_down);

extern void w800_wifi_reinstall_cb(void);

/* 获取RTC记录的当前时间 */
csi_rtc_time_t Rtc_GetCurrentTime(void)
{
	if(CSI_OK != csi_rtc_get_time(&rtc, &current_time))
	{
		printf("===%s, %d\n", __FUNCTION__, __LINE__);
	}
	
	return current_time;
}

void Rtc_UpdateTimeFromIot(int ts)
{
	struct my_tm iot_t;
	ts += 8*3600;  /* +8h beijing time */
	my_localtime(ts, &iot_t); 
	iot_t.my_tm_mon += 1; 
	Rtc_UpdateFromNtp(iot_t);
	setting_time.tm_sec = iot_t.my_tm_sec;
	setting_time.tm_min = iot_t.my_tm_min;
	setting_time.tm_hour = iot_t.my_tm_hour;
	setting_time.tm_mday = iot_t.my_tm_mday;
	setting_time.tm_mon = iot_t.my_tm_mon-1;
	setting_time.tm_year = iot_t.my_tm_year-1900;
}

/* 获取RTC记录的当前时间 */
csi_rtc_time_t Rtc_GetSettingTime(void)
{
	return setting_time;
}

uint8_t Rtc_GetNtpSuccessFlg(void)
{
	// 如果ntp_enable_flg==0则不使用ntp更新时间，则不获取网络时间，返回1，否则返回ntp校时是否成功标志
	return (ntp_enable_flg == 0) ? 1 : ntp_udt;
}


/* 设置NTP使能标志 */
void Rtc_SetNtpEnStatus(uint8_t en_flg)
{
	ntp_enable_flg = en_flg;
    Kv_WriteData(KV_NTP, en_flg);
}

/* 获取NTP初始化完成状态 */
uint8_t Rtc_GetNtpInitFishFlg(void)
{
	return ntp_init_flg;
}

/* 获取NTP使能状态 */
uint8_t Rtc_GetNtpEnStatus(void)
{
	return ntp_enable_flg;
}

void Rtc_Init(void)
{
	int task_s;
	csi_rtc_time_t time_init;
	
	if(CSI_OK != csi_rtc_init(&rtc, 0))
	{
		printf("===%s, %d\n", __FUNCTION__, __LINE__);
	}
	
    time_init.tm_year = 122;   /* 2022年 */
    time_init.tm_mon = 2;      /* 3月 */
    time_init.tm_mday = 6;     /* 6日 */
    time_init.tm_hour = 14;
    time_init.tm_min  = 59;
    time_init.tm_sec  = 40;
	time_init.tm_wday  = 0;
	
	if(CSI_OK != csi_rtc_set_time(&rtc, &time_init))
	{
		printf("===%s, %d\n", __FUNCTION__, __LINE__);
	}
#if (RTC_DEBUG == STD_ON)
	/* RTC 任务 */
	task_s = aos_task_new_ext(&rtc_task_h,
								"rtc_task", 
								Rtc_TaskEntry, 
								NULL, 
								512, 
								5);
	if (0 == task_s)
	{
		printf("RTC Task create success\r\n");
	}
	else
	{
		printf("RTC Task create failed\r\n");
	}
#endif
	/* 如果使能NTP没有被设置过，则默认使能 */
    if (1 == Kv_GetSaveStatus(KV_NTP))
    {
		/* 获取设置状态 */
		ntp_enable_flg = Kv_ReadData(KV_NTP);
	}

	/* NTP 任务 */
	task_s = aos_task_new_ext(&ntp_task_h,
							"ntp_task", 
							Rtc_NtpTaskEntry, 
							NULL, 
							2048, 
							0);
	if (0 == task_s)
	{
		printf("NTP Task create success\r\n");
	}
	else
	{
		printf("NTP Task create failed\r\n");
	}

	ntp_init_flg = 1;
}

#if (RTC_DEBUG == STD_ON)
/* RTC 时间更新任务 */
static void Rtc_TaskEntry(void *arg)
{
	while(1)
	{
		if(CSI_OK != csi_rtc_get_time(&rtc, &current_time))
		{
			printf("===%s, %d\n", __FUNCTION__, __LINE__);
		}
#if (RTC_NTP_DEBUG == STD_ON)
		printf("Time:%d-%d-%d  ", current_time.tm_year+1900, current_time.tm_mon+1, current_time.tm_mday);
		printf("%d:%d:%d\n", current_time.tm_hour, current_time.tm_min, current_time.tm_sec);
#endif
		aos_msleep(1000);
	}
}
#endif

/* NTP 时间更新任务 */
static void Rtc_NtpTaskEntry(void *arg)
{
	int ret;
	static time_t now_time;
	struct my_tm ntp_t;
	static uint8_t last_ntp_enable_flg = 0;
#if 0
	struct tm* t2;
#endif

	while(1)
	{
		/* 如果ntp使能状态由禁能变为使能，则重新进行校时 */
		if ((last_ntp_enable_flg != ntp_enable_flg) && (ntp_enable_flg == 1))
		{
							printf("\n=--------------------------\n");
			w800_wifi_reinstall_cb();
			ntp_udt = 0;
		}

		/* 连接网络成功，获取到了IP */
		if ((1 == netmgr_is_gotip(Net_GetnetMgrHdl())) &&
			(1 == ntp_enable_flg))
		{
			/* if IP id got then create the task to get ntp time */
			if (0 == ntp_udt)
			{
				// w800_wifi_reinstall_cb();

				ret = ntp_sync_time(NULL);
#if (RTC_NTP_DEBUG == STD_ON)
				if(ret == 0)
					printf("--------Get NTP success\n");
				else
					printf("--------Get NTP failed\n");
#else
				(void)ret;
#endif
				ret = ntp_get_time(&now_time);
#if 0
				/* 无需进行判断 */
				if(1 == Rtc_NtpTimestampJudge(now_time))
				{
#endif
					now_time += 8*3600;  /* +8h beijing time */
					my_localtime(now_time, &ntp_t);
					/* 月份是从0开始的，需要加1 */
					ntp_t.my_tm_mon += 1; 
					if(1 == Rtc_UpdateFromNtp(ntp_t))
					{
						Weather_SetUpdateFlg();
						ntp_udt = 1;
					}
#if 0
				}
#endif
				
#if (RTC_NTP_DEBUG == STD_ON)
				my_localtime(now_time, &ntp_t);
				printf("Date-Time:%04d-%02d-%02d %02d:%02d:%02d\r\n",ntp_t.my_tm_year,ntp_t.my_tm_mon+1,ntp_t.my_tm_mday,ntp_t.my_tm_hour,ntp_t.my_tm_min,ntp_t.my_tm_sec);
				ntp_t.my_tm_mon += 1;
				printf("%d---%d\r\n", my_mktime(ntp_t), now_time);
#endif
			}
			
			/* 修改为上电的时候进行一次NTP校准即可 */	
#if 0    
			cur_ts = Rtc_GetCurTimeStamp();
			/* 每24小时使用NTP校准一次 */
			if((cur_ts - Last_ntp_ts) > ONE_DAY_TIMESTAMP)
			{
				ntp_udt = 0;
				ntp_task_cycle_ms = 10000;    /* 10s */
			}
			else
			{
				ntp_udt = 1;
				ntp_task_cycle_ms = 60000;    /* 1 min */
			}
#endif
		}
		else
		{
			/* Do Nothing */
		}
		
		last_ntp_enable_flg = ntp_enable_flg;
		aos_msleep(ntp_task_cycle_ms);
	}
}

static time_t Rtc_GetCurTimeStamp(void)
{
	struct my_tm cur_t;
	
	(void)Rtc_GetCurrentTime();    /* 获取当前时间 */
	
	cur_t.my_tm_year = current_time.tm_year+1900;
	cur_t.my_tm_mon = current_time.tm_mon+1;
	cur_t.my_tm_mday = current_time.tm_mday;
	cur_t.my_tm_hour = current_time.tm_hour;
	cur_t.my_tm_min = current_time.tm_min;
	cur_t.my_tm_sec = current_time.tm_sec;
	//	printf("Date-Time:%04d-%02d-%02d %02d:%02d:%02d\r\n",cur_t.my_tm_year,cur_t.my_tm_mon,cur_t.my_tm_mday,cur_t.my_tm_hour,cur_t.my_tm_min,cur_t.my_tm_sec);
	
	/* 计算当前时间的时间戳 */
	return my_mktime(cur_t);
}

/* 判断获取的时间戳是不是正确的 */
static uint8_t Rtc_NtpTimestampJudge(time_t para_ts)
{
	time_t tmp_cur_ts;
	
	/* 获取当前系统时间，判断返回的系统时间戳是否等于或者大于当前时间 */
	tmp_cur_ts = Rtc_GetCurTimeStamp();
	Last_ntp_ts = para_ts;    /* 记录使用NTP时的时间戳 */
	// printf("Timestamp: %d, %d\r\n", tmp_cur_ts, para_ts);
	/* 当前系统时间戳小于NTP获取的时间戳，表示需要更新时间
	   否则不更新时间*/
	if(tmp_cur_ts < para_ts)
	{
		return 1;
	}
	else
	{
		return 0;
	}
}

/* 更新NTP时间到RTC */
static uint8_t Rtc_UpdateFromNtp(const struct my_tm ntp_t)
{
	current_time.tm_year = ntp_t.my_tm_year - 1900;
	current_time.tm_mon  = ntp_t.my_tm_mon - 1;
	current_time.tm_mday = ntp_t.my_tm_mday;
	current_time.tm_hour = ntp_t.my_tm_hour;
	current_time.tm_min  = ntp_t.my_tm_min;
	current_time.tm_sec  = ntp_t.my_tm_sec;
	
	csi_rtc_uninit(&rtc);
	
	if(CSI_OK != csi_rtc_init(&rtc, 0))
	{
		printf("===%s, %d\n", __FUNCTION__, __LINE__);
		return 0;
	}
	
	if(CSI_OK != csi_rtc_set_time(&rtc, &current_time))
	{
		printf("===%s, %d\n", __FUNCTION__, __LINE__);
		
		return 0;
	}

	printf("Update RTC success\r\n");
	
	return 1;
}

/* 时间手动设置 */
void Rtc_Setting(e_RtcSetting setting_flg, uint8_t setting_dir)
{
	uint16_t tmp_dt;
	
	switch(setting_flg)
	{
		case RTC_SETTING_YEAR:
		{ 
			tmp_dt = setting_time.tm_year;
			Rtc_SettingLgc(&tmp_dt, setting_dir, 199, 70);
			setting_time.tm_year = tmp_dt;
			break;
		}	
		case RTC_SETTING_MON:
		{
			tmp_dt = setting_time.tm_mon;
			Rtc_SettingLgc(&tmp_dt, setting_dir, 11, 0);
			setting_time.tm_mon = tmp_dt;	
			break;
		}
		case RTC_SETTING_MDAY:
		{
			tmp_dt = setting_time.tm_mday;
			/* 闰年 且 2月 */
			if((isleap(setting_time.tm_year+1900)) && (1 == setting_time.tm_mon))
				Rtc_SettingLgc(&tmp_dt, setting_dir, 29, 1);
			else
				Rtc_SettingLgc(&tmp_dt, setting_dir, Days[setting_time.tm_mon], 1);
			setting_time.tm_mday = tmp_dt;	
			break;
		}
		case RTC_SETTING_HOUR:
		{
			tmp_dt = setting_time.tm_hour;
			Rtc_SettingLgc(&tmp_dt, setting_dir, 23, 0);
			setting_time.tm_hour = tmp_dt;		
			break;
		}
		case RTC_SETTING_MIN:
		{
			tmp_dt = setting_time.tm_min;
			Rtc_SettingLgc(&tmp_dt, setting_dir, 59, 0);
			setting_time.tm_min = tmp_dt;		
			break;
		}
		case RTC_SETTING_SEC:
		{
			tmp_dt = setting_time.tm_sec;
			Rtc_SettingLgc(&tmp_dt, setting_dir, 59, 0);
			setting_time.tm_sec = tmp_dt;		
			break;
		}
	}
}

/* 时间设置逻辑 */
static void Rtc_SettingLgc(uint16_t *dt, uint8_t setting_dir, uint16_t limit_up, uint16_t limit_down)
{	
	if(RTC_SETTING_UP == setting_dir)
	{
		if(*dt >= limit_up)
		{
			*dt = limit_down;
		}
		else
		{
			*dt = *dt + 1;
		}
	}
	else if(RTC_SETTING_DOWN == setting_dir)
	{
		if(*dt <= limit_down)
		{
			*dt = limit_up;
		}
		else
		{
			*dt = *dt - 1;
		}
	}
}

/* 手动更新时间 */
uint8_t Rtc_UpdateTimeManual(void)
{
	current_time.tm_year = setting_time.tm_year;
	current_time.tm_mon  = setting_time.tm_mon;
	current_time.tm_mday = setting_time.tm_mday;
	current_time.tm_hour = setting_time.tm_hour;
	current_time.tm_min  = setting_time.tm_min;
	current_time.tm_sec  = setting_time.tm_sec;
	
	csi_rtc_uninit(&rtc);
	
	if(CSI_OK != csi_rtc_init(&rtc, 0))
	{
		printf("===%s, %d\n", __FUNCTION__, __LINE__);
		return 0;
	}
	
	if(CSI_OK != csi_rtc_set_time(&rtc, &current_time))
	{
		printf("===%s, %d\n", __FUNCTION__, __LINE__);
		
		return 0;
	}

	printf("Update RTC success\r\n");
	
	return 1;
}

/* 从当前时钟更新时间 */
void Rtc_UpdateSetFromCurTime(void)
{
	setting_time.tm_year = current_time.tm_year;
	setting_time.tm_mon = current_time.tm_mon;
	setting_time.tm_mday = current_time.tm_mday;
	setting_time.tm_hour = current_time.tm_hour;
	setting_time.tm_min = current_time.tm_min;
	setting_time.tm_sec = current_time.tm_sec;
}

/* 根据日期计算星期 -- 泰勒公式 
 * 
 * 返回值：0-星期日， 1-6：星期一~星期六*/
uint8_t Rtc_CalculateWeekday(uint16_t y, uint8_t m, uint8_t d)
{
	uint32_t wd;
	uint8_t  y_post;
	uint8_t  y_pre;
	
	if(m <= 2)
	{
		m += 12;	
		y --; 
	}
		
	y_post = y % 100;
	y_pre  = y / 100;
	
	wd = y_post + (y_post/4) + (y_pre/4) - 2*y_pre + ((13*(m+1))/5) + d - 1;

	wd %= 7;
	
	return wd;
}

/* 时间戳转换为时间
 * 为什么要自己写这个函数而不是用系统自带的呢?
 * 当单独使用NTP获取了时间戳之后使用ctime函数可以正确转换成时间，但是使用了RTC时候不能正确的
 * 将时间戳转换成时间，所以自己写了一个时间戳转换成时间的函数，可以正确将时间戳转换成时间
 * */
void my_localtime(time_t time, struct my_tm *ntp_t)
{
    unsigned int Pass4year;
    int hours_per_year;
 
    if(time < 0)
    {
        time = 0;
    }
    //取秒时间
    ntp_t->my_tm_sec=(int)(time % 60);
    time /= 60;
    //取分钟时间
    ntp_t->my_tm_min=(int)(time % 60);
    time /= 60;
    //取过去多少个四年，每四年有 1461*24 小时
    Pass4year=((unsigned int)time / (1461L * 24L));
    //计算年份
    ntp_t->my_tm_year=(Pass4year << 2) + 1970;
    //四年中剩下的小时数
    time %= 1461L * 24L;
    //校正闰年影响的年份，计算一年中剩下的小时数
    for (;;)
    {
        //一年的小时数
        hours_per_year = 365 * 24;
        //判断闰年
        if ((ntp_t->my_tm_year & 3) == 0)
        {
            //是闰年，一年则多24小时，即一天
            hours_per_year += 24;
        }
        if (time < hours_per_year)
        {
            break;
        }
        ntp_t->my_tm_year++;
        time -= hours_per_year;
    }
    //小时数
    ntp_t->my_tm_hour=(int)(time % 24);
    //一年中剩下的天数
    time /= 24;
    //假定为闰年
    time++;
    //校正闰年的误差，计算月份，日期
    if((ntp_t->my_tm_year & 3) == 0)
    {
        if (time > 60)
        {
            time--;
        }
        else
        {
            if (time == 60)
            {
                ntp_t->my_tm_mon = 1;
                ntp_t->my_tm_mday = 29;
                return ;
            }
        }
    }
    //计算月日
    for (ntp_t->my_tm_mon = 0; Days[ntp_t->my_tm_mon] < time;ntp_t->my_tm_mon++)
    {
        time -= Days[ntp_t->my_tm_mon];
    }
 
    ntp_t->my_tm_mday = (int)(time);
}
 
/* 日期转换为时间戳 */
time_t my_mktime(struct my_tm dt)
{
    time_t result;
    int i =0;
    // 以平年时间计算的秒数
    result = (dt.my_tm_year - 1970) * 365 * 24 * 3600 +
		(mon_yday[isleap(dt.my_tm_year)][dt.my_tm_mon-1] + dt.my_tm_mday - 1) * 24 * 3600 +
		dt.my_tm_hour * 3600 + dt.my_tm_min * 60 + dt.my_tm_sec;
    // 加上闰年的秒数
    for(i=1970; i < dt.my_tm_year; i++)
    {
        if(isleap(i))
        {
            result += 24 * 3600;
        }
    }
    return(result);
}

/* 检测是否是闰年 */
static int isleap(int year)
{
    return (year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0);
} 
